import { KBinary } from "../binary/KBinary";
import { KWriteOp } from "../binary/KWriteOp";
import { ObjCoder } from "./ObjCoder";
import { StructValueType } from "./StructValueType";

const SVT = StructValueType;

/** 结构字段 */
export interface IStructField extends Array<any> {
    /** 字段名(+?代表允许为空) */
    [0]: string,
    /** 字段类型 */
    [1]: StructValueType,
    /** 数组维度 */
    [2]?: number,
    /** 类型参数 */
    [3]?: number | IStructField[]
}

/** 结构编解码 */
export class StructCoder {

    /** 编码 */
    encode: (obj: any, bin?: KBinary) => KBinary;
    /** 解码 */
    decode: (bufOrBin: Uint8Array | KBinary, out?: any) => any;

    constructor(struct: IStructField[]) {

        let encodeFns: ((obj: any) => void)[],
            decodeFns: ((obj: any) => void)[];

        this.encode = (obj: any, bin?: KBinary) => {
            encodeFns = buildEncodeFns(struct);
            return (this.encode = (obj: any, bin?: KBinary) => {
                ctx.b = bin || b.wStart();
                for (let i = 0; i < encodeFns.length; ++i) {
                    encodeFns[i](obj);
                }
                return ctx.b;
            })(obj, bin);
        }

        this.decode = (bufOrBin: Uint8Array | KBinary, out?: any) => {
            decodeFns = buildDecodeFns(struct);
            return (this.decode = (bufOrBin: Uint8Array | KBinary, out?: any) => {
                ctx.b = bufOrBin instanceof Uint8Array ? b.rStart(bufOrBin) : bufOrBin;
                const obj = out || {};
                if (ctx.b.rHasNext) {
                    for (let i = 0; i < decodeFns.length; ++i) {
                        decodeFns[i](obj);
                    }
                }
                b.rEnd();
                return obj;
            })(bufOrBin, out);
        }
    }

}


const b = new KBinary();
const ctx: { b: KBinary } = {} as any;

function preStruct(_struct: IStructField[]) {
    const struct: IStructField[] = [];
    const flagFields: string[] = [];
    const flagFieldDic: { [field: string]: number } = {};
    for (let i = 0; i < _struct.length; ++i) {
        let [field, valueType, arrVec, param] = _struct[i];
        if (valueType === SVT.bool) {
            field = field.replace(/\?/g, "");
            flagFields.push(field);
            flagFieldDic[field] = 1;
        } else if (field.includes("?")) {
            field = field.replace(/\?/g, "");
            flagFields.push(field);
            flagFieldDic[field] = 2;
        }
        struct.push([field, valueType, arrVec, param]);
    }
    return { struct, flagFields, flagFieldDic, flagLen: flagFields.length > 0 ? Math.ceil(flagFields.length / 8) : 0 };
}

// ============ ENCODE ============

function buildEncodeFns(_struct: IStructField[]) {

    const { struct, flagFields, flagFieldDic, flagLen } = preStruct(_struct);

    const encodeFns: ((obj: any) => void)[] = [];
    if (flagLen) {
        //标志位，长度每8个字段增长1字节
        //用于判断bit对应字段是否有值，或者bool的真假
        const flagCtx = { flag: 0 };
        const flagOpFns: ((obj: any, setFlag: () => void) => void)[] = [];
        const fieldLen = flagFields.length - 1;
        for (let i = 0; i <= fieldLen; ++i) {
            const field = flagFields[i],
                hasField = 1 << (i % 8);
            i % 8 === 0 && flagOpFns.push(() => flagCtx.flag = 0);
            flagFieldDic[field] === 1 ?
                flagOpFns.push(obj => obj[field] && (flagCtx.flag |= hasField)) :
                flagOpFns.push(obj => obj[field] != undefined && (flagCtx.flag |= hasField));
            if (i % 8 === 7 || i === fieldLen) flagOpFns.push((_, setFlag) => setFlag());
        }
        const flagOp = (buf: Uint8Array, pos: number, obj: any) => {
            const setFlag = () => buf[pos++] = flagCtx.flag;
            for (let i = 0; i < flagOpFns.length; ++i) {
                flagOpFns[i](obj, setFlag);
            }
            return pos;
        };
        encodeFns.push(obj => ctx.b.wAddOp(flagOp, obj, flagLen));
    }

    //开始遍历字段
    for (let i = 0; i < struct.length; ++i) {
        let [field, valueType, arrVec, param] = struct[i];
        let checkField: (obj: any) => any,
            pushEncodeFn: (encode: (val: any) => void) => void,
            pushEncodeArrFn: (encodeArr: (arr: any[]) => void) => void;

        if (flagFieldDic[field]) {
            checkField = valueType === SVT.bool ? obj => obj[field] : obj => obj[field] != undefined;
        } else {
            checkField = obj => {
                if (obj[field] == undefined) throw new Error(`StructCoder:${field}字段不能为空值`);
                return true;
            };
        }

        if (!arrVec || arrVec <= 0) {
            pushEncodeFn = (encode: (val: any) => void) => {
                encodeFns.push(obj => checkField(obj) && encode(obj[field]));
            };
        } else {
            pushEncodeArrFn = (encodeArr: (arr: any[]) => void) => {
                if (arrVec == 1) {
                    encodeFns.push(obj => {
                        if (checkField(obj)) {
                            //[写入] >>> 数组长度
                            ctx.b.wAuint32(obj[field].length);
                            encodeArr(obj[field])
                        }
                    });
                } else {
                    //多维数组
                    encodeFns.push(obj => checkField(obj) && encodeVecArr(encodeArr, obj[field], arrVec));
                }
            };

            pushEncodeFn = (encode: (val: any) => void) => {
                pushEncodeArrFn(arr => {
                    for (let i = 0; i < arr.length; ++i) {
                        encode(arr[i]);
                    }
                })
            };
        }

        if (pushEncodeArrFn && encodeArrFnDic[valueType]) {
            pushEncodeArrFn(encodeArrFnDic[valueType]);
            continue;
        }

        if (encodeFnDic[valueType]) {
            pushEncodeFn(encodeFnDic[valueType]);
            continue;
        }

        if (typeof param === "number") param = Math.pow(10, param);
        if (encodeWithParamFnDic[valueType]) {
            pushEncodeFn(encodeWithParamFnDic[valueType](param));
            continue;
        }
    }
    return encodeFns;
};

function encodeVecArr(encode: (arr: any[]) => void, arr: any[], arrVec: number) {
    //[写入] >>> 数组长度
    ctx.b.wAuint32(arr.length);
    if (--arrVec) {
        //还在外层,递归
        for (let i = 0; i < arr.length; ++i) {
            encodeVecArr(encode, arr[i], arrVec);
        }
    } else {
        //已经是最里层了,编码数据
        encode(arr);
    }
}

const encodeFnDic = {
    [SVT.byte]: (val: any) => ctx.b.wByte(val),
    [SVT.bytes]: (val: any) => ctx.b.wBytes(val),
    [SVT.uint8]: (val: any) => ctx.b.wUint8(val),
    [SVT.uint16]: (val: any) => ctx.b.wUint16(val),
    [SVT.uint32]: (val: any) => ctx.b.wUint32(val),
    [SVT.uint64]: (val: any) => ctx.b.wUint64(val),
    [SVT.auint32]: (val: any) => ctx.b.wAuint32(val),
    [SVT.auint64]: (val: any) => ctx.b.wAuint64(val),
    [SVT.int8]: (val: any) => ctx.b.wInt8(val),
    [SVT.int16]: (val: any) => ctx.b.wInt16(val),
    [SVT.int32]: (val: any) => ctx.b.wInt32(val),
    [SVT.int64]: (val: any) => ctx.b.wInt64(val),
    [SVT.aint32]: (val: any) => ctx.b.wAint32(val),
    [SVT.aint64]: (val: any) => ctx.b.wAint64(val),
    [SVT.float32]: (val: any) => ctx.b.wFloat32(val),
    [SVT.floatStr]: (val: any) => ctx.b.wStr(val.toString()),
    [SVT.str]: (val: any) => ctx.b.wStr(val),
    [SVT.any]: (val: any) => ObjCoder.enAny(ctx.b, val),
    [SVT.obj]: (val: any) => ObjCoder.enObj(ctx.b, val),
    [SVT.arr]: (val: any) => ObjCoder.enArr(ctx.b, val)
} as any;

const encodeBoolArrOp = (buf: Uint8Array, pos: number, arr: boolean[]) => {
    const flagLen = 1 + (arr.length / 8 >> 0);
    for (let flagIdx = 0; flagIdx < flagLen; ++flagIdx) {
        let flag = 0;
        for (let i = flagIdx * 8 + 8, min = flagIdx * 8; --i >= min;) {
            //值为true时把标志位对应bit设为1
            if (arr[i]) flag |= 1 << (i % 8);
        }
        buf[pos++] = flag;
    }
    return pos;
};
const encodeArrFnDic = {
    [SVT.bool]: (arr: any[]) => {
        //bool标志位，用来判断bit对应bool的真假
        let flagLen = 1 + (arr.length / 8 >> 0);
        //[写入] >>> bool标志位
        ctx.b.wAddOp(encodeBoolArrOp, arr, flagLen);
    },
    [SVT.byte]: (arr: any[]) => ctx.b.wAddOp(KWriteOp.bytes, arr, arr.length)
} as any;

const encodeWithParamFnDic = {
    [SVT.floatAuint32]: (param: any) => (val: any) => ctx.b.wAuint32(Math.floor(val * param)),
    [SVT.floatAuint64]: (param: any) => (val: any) => ctx.b.wAuint64(Math.floor(val * param)),
    [SVT.floatAint32]: (param: any) => (val: any) => ctx.b.wAint32(val > 0 ? Math.floor(val * param) : Math.ceil(val * param)),
    [SVT.floatAint64]: (param: any) => (val: any) => ctx.b.wAint64(val > 0 ? Math.floor(val * param) : Math.ceil(val * param)),
    [SVT.struct]: (param: any) => {
        const encodeFns = buildEncodeFns(param);
        return (val: any) => {
            for (let i = 0; i < encodeFns.length; ++i) {
                encodeFns[i](val);
            }
        }
    }
} as any;

// ============ DECODE ============

function buildDecodeFns(_struct: IStructField[]) {

    const { struct, flagFields, flagFieldDic, flagLen } = preStruct(_struct);

    const flagCtx = {} as { buf: Uint8Array },
        decodeFns: ((obj: any) => any)[] = [];

    if (flagLen) {
        //读出标志位
        decodeFns.push(() => flagCtx.buf = ctx.b.rCut(flagLen));
    }

    //开始遍历字段
    for (let idx = 0; idx < struct.length; ++idx) {
        let [field, valueType, arrVec, param] = struct[idx];

        let checkFlag: () => any,
            pushDecodeFn: (decode: () => any) => void,
            pushDecodeArrFn: (decodeArr: (len: number) => any[]) => void

        if (flagFieldDic[field]) {
            const idx = flagFields.indexOf(field),
                flagIdx = idx / 8 >> 0,
                hasField = 1 << idx % 8;
            checkFlag = () => flagCtx.buf[flagIdx] & hasField;
        }

        if (!arrVec || arrVec <= 0) {

            if (valueType === SVT.bool) {
                decodeFns.push(obj => obj[field] = checkFlag() ? true : false);
                continue;
            }

            pushDecodeFn = (decode: () => any) => {
                decodeFns.push(checkFlag ?
                    obj => checkFlag() && (obj[field] = decode()) :
                    obj => obj[field] = decode());
            }
        } else {
            pushDecodeArrFn = (decodeArr: (len: number) => any[]) => {
                if (arrVec === 1) {
                    decodeFns.push(checkFlag ?
                        obj => checkFlag() && (obj[field] = decodeArr(ctx.b.rAuint32())) :
                        obj => obj[field] = decodeArr(ctx.b.rAuint32()));
                } else {
                    //多维数组
                    decodeFns.push(checkFlag ?
                        obj => checkFlag() && (obj[field] = decodeVecArr(decodeArr, arrVec)) :
                        obj => obj[field] = decodeVecArr(decodeArr, arrVec));
                }
            };

            pushDecodeFn = (decode: () => any) => {
                pushDecodeArrFn(len => {
                    let arr = [];
                    while (--len >= 0) {
                        arr.push(decode());
                    }
                    return arr;
                });
            };
        }

        if (pushDecodeArrFn && decodeArrFnDic[valueType]) {
            pushDecodeArrFn(decodeArrFnDic[valueType]);
            continue;
        }

        if (decodeFnDic[valueType]) {
            pushDecodeFn(decodeFnDic[valueType]);
            continue;
        }

        if (typeof param === "number") param = Math.pow(10, param);
        if (decodeWithParamFnDic[valueType]) {
            pushDecodeFn(decodeWithParamFnDic[valueType](param));
            continue;
        }
    }
    return decodeFns;
};

function decodeVecArr(decodeArr: (len: number) => any[], arrVec: number): any[] {
    //[读取] >>> 数组长度
    let len = ctx.b.rAuint32();
    if (--arrVec) {
        //还在外层,递归
        let arr = [];
        while (--len >= 0) {
            arr.push(decodeVecArr(decodeArr, arrVec));
        }
        return arr;
    } else {
        //已经是最里层了,解码数据
        return decodeArr(len);
    }
}

const decodeFnDic = {
    [SVT.byte]: () => ctx.b.rByte(),
    [SVT.bytes]: () => ctx.b.rBytes(),
    [SVT.uint8]: () => ctx.b.rUint8(),
    [SVT.uint16]: () => ctx.b.rUint16(),
    [SVT.uint32]: () => ctx.b.rUint32(),
    [SVT.uint64]: () => ctx.b.rUint64(),
    [SVT.auint32]: () => ctx.b.rAuint32(),
    [SVT.auint64]: () => ctx.b.rAuint64(),
    [SVT.int8]: () => ctx.b.rInt8(),
    [SVT.int16]: () => ctx.b.rInt16(),
    [SVT.int32]: () => ctx.b.rInt32(),
    [SVT.int64]: () => ctx.b.rInt64(),
    [SVT.aint32]: () => ctx.b.rAint32(),
    [SVT.aint64]: () => ctx.b.rAint64(),
    [SVT.float32]: () => ctx.b.rFloat32(),
    [SVT.floatStr]: () => Number(ctx.b.rStr()),
    [SVT.str]: () => ctx.b.rStr(),
    [SVT.any]: () => ObjCoder.deAny(ctx.b),
    [SVT.obj]: () => ObjCoder.deObj(ctx.b),
    [SVT.arr]: () => ObjCoder.deArr(ctx.b)
} as any;

const decodeArrFnDic = {
    [SVT.bool]: (len: number) => {
        //[读取] >>> bool标志位 用于判断位对应真假
        const flagLen = 1 + (len / 8 >> 0);
        const arr: any[] = [];
        for (let flagIdx = 0; flagIdx < flagLen; flagIdx++) {
            const flag = ctx.b.rByte();
            for (let i = flagIdx * 8, max = Math.min(i + 8, len); i < max; ++i) {
                arr.push(flag & 1 << i ? true : false);
            }
        }
        return arr;
    }
} as any;

const decodeWithParamFnDic = {
    [SVT.floatAuint32]: (param: any) => () => ctx.b.rAuint32() / param,
    [SVT.floatAuint64]: (param: any) => () => ctx.b.rAuint64() / param,
    [SVT.floatAint32]: (param: any) => () => ctx.b.rAint32() / param,
    [SVT.floatAint64]: (param: any) => () => ctx.b.rAint64() / param,
    [SVT.struct]: (param: any) => {
        const decodeFns = buildDecodeFns(param);
        return () => {
            const obj = {};
            for (let i = 0; i < decodeFns.length; ++i) {
                decodeFns[i](obj);
            }
            return obj;
        }
    }
} as any;